package annotool.clustering;


import java.io.Writer;
import java.lang.Math;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;


public class Hough 
{
	public static final int HOUGH_THRESHOLD = 1;
	public static final int NUMBER_OF_BINS_PER_DIMENSION = 16;
	public int featureSize;
	float[] input;
	float[] output;
	int width;
	int height;
	double maxscale;
	float[] maxxloc,
	maxyloc;
	int orientationbinsize;
	int[] scalebinsize,
	count;
	int [] locationbinsize;
	float[] scale, clonescale, orientation,	xlocation, ylocation;
	int hough_Th, hough_Scale, hough_Orie, hough_Loc;
	ArrayList<Integer> scaleloc = new ArrayList<Integer> ();
	//int accSize=30;
	float[][] results;
	Writer outputfile  = null;
	public Hough(Writer outputfile, float[] fs, int featureSize, int height, int width, int hough_Th, int hough_Scale, int hough_Orie, int hough_Loc) 
	{
		this.outputfile = outputfile;
		this.featureSize = featureSize;
		this.input = fs;
		this.height = height;
		this.width = width;
		this.hough_Loc = hough_Loc;
		this.hough_Orie = hough_Orie;
		this.hough_Scale = hough_Scale;
		this.hough_Th = hough_Scale;
	}
	public Hough(float[] fs, int featureSize, int height, int width, int hough_Th, int hough_Scale, int hough_Orie, int hough_Loc) 
	{
		this.featureSize = featureSize;
		this.input = fs;
		this.height = height;
		this.width = width;
		this.hough_Loc = hough_Loc;
		this.hough_Orie = hough_Orie;
		this.hough_Scale = hough_Scale;
		this.hough_Th = hough_Scale;
		//scale = new float[fs.length / featureSize];
		//clonescale = new float[fs.length / featureSize];
		//orientation = new float[fs.length / featureSize];
		//xlocation = new float[fs.length / featureSize];
		//ylocation = new float[fs.length / featureSize];
		//calculateParameters(fs);
	}
	public float[] H()
	{
		//maxscale = getMaxScale(scale);
		float[] features = initializeAccu(input);
		return features;
	}

	private float[] initializeAccu(float[] input2) 
	{
		double maxOrientations = input2[1];
		double minOrientations = input2[1];
		int base = 0;
		//ArrayList<Float> orientations = new ArrayList<Float>();
		for (int j = 0; j < input2.length / featureSize; j++)
		{
			// to find the maximum
			if(maxOrientations < input2[(j * featureSize) + 1])
			{
				maxOrientations = input2[(j * featureSize) + 1];
			}
			// to find the minimum
			if(minOrientations > input2[(j * featureSize) + 1])
			{
				minOrientations = input2[(j * featureSize) + 1];
			}
		}
		/* -- modified dec1
		// adding all the orientations into an arraylist
		for (int j = 0; j < input2.length / featureSize; j++)
		{
			orientations.add(input2[(j * featureSize) + 1]);
		}

		// for finding the min and max orientation
		Collections.sort(orientations);
		maxOrientations = orientations.get(orientations.size() - 1);
		minOrientations = orientations.get(0);
		 */
		//System.out.println("the oriemaxperinput is : "+maxOrientations+" and the min is "+minOrientations);
		int orientation1 = (int) Math.floor((minOrientations ) / hough_Orie);
		int orientation2 = (int) Math.ceil((maxOrientations ) / hough_Orie );
		orientationbinsize = orientation2 - orientation1;
		// TODO Auto-generated method stub
		float[] features;
		ArrayList<ArrayList> inputperorientation = new ArrayList<ArrayList>(orientationbinsize);
		ArrayList<Integer> finalindexes = new ArrayList<Integer> ();
		count = new int[orientationbinsize];

		ArrayList<ArrayList> accorientation = new ArrayList<ArrayList>();
		ArrayList<ArrayList<HashMap<Integer, Integer>>> accAccumulator = new ArrayList<ArrayList<HashMap<Integer, Integer>>>();

		//int[] mainindex = new int[input2.length / featureSize];
		// dec 09
		//int[][] mainindex = new int[orientationbinsize][input2.length / featureSize];
		int[] mainindex = new int[input2.length/featureSize];

		//System.out.println("the orie "+orientationbinsize);
		for (int i = 0; i < orientationbinsize; i++)
		{
			ArrayList<ArrayList> inputsofxloc = new ArrayList<ArrayList>();
			ArrayList<ArrayList> inputsofyloc = new ArrayList<ArrayList>();
			ArrayList<ArrayList> inputsofindex = new ArrayList<ArrayList>();
			//	ArrayList<Integer> accscaleloc = new ArrayList<Integer> ();
			ArrayList<HashMap<Integer, Integer>> accumlatorPerScale = new ArrayList<HashMap<Integer, Integer>>();
			//HashMap<Integer, Integer> accumulatorMap = new HashMap<Integer, Integer>();
			//added 2/4/11
			int totalCount = 0;
			//
			//System.out.println("the orientation bin number is "+i);
			ArrayList<Integer> inputsindexoforientation = new ArrayList<Integer>();
			ArrayList<Float> inputsindexofscale = new ArrayList<Float> ();

			for (int j = 0; j < input2.length / featureSize; j++)
			{
				//input2[(j * featureSize) + 1] = input2[(j * featureSize) + 1] * 36;
				//if(input2[(j * featureSize) + 1] < 0)
				//input2[(j * featureSize) + 1] = 360 - input2[(j * featureSize) + 1];
				//if (((input2[(j * featureSize) + 1] / 30) >= i) && ((input2[(j * featureSize) + 1] / 30) < i+1))
				if (((input2[(j * featureSize) + 1]) / hough_Orie >= (i + orientation1)) && ((input2[(j * featureSize) + 1] / hough_Orie) < (i + orientation1)+1))
				{
					//System.out.println("orie :"+(i + orientation1)+" : "+(i + orientation1+ 1) +" or is "+(input2[(j * featureSize) + 1] / 3));
					inputsindexoforientation.add(j);
					inputsindexofscale.add(input2[(j * featureSize) + 0]);
				}
			}
			//mainindex[i] = new int[inputsindexoforientation.size()];	//dec 09
			//System.out.println("out the "+inputsindexofscale+" and the size is  "+inputsindexofscale.size());
			//ArrayList<Float> copyinputsindexofscale = new ArrayList<Float> (inputsindexofscale);
			//System.out.println("all the scales: "+inputsindexofscale);
			double scalemaxperinput, scaleminperinput;
			int scalebinsperinput1, scalebinsperinput2;
			if(inputsindexofscale.size() != 0)
			{
				//Collections.copy(copyinputsindexofscale, inputsindexofscale);
				//Collections.sort(inputsindexofscale);
				scalemaxperinput = Collections.max(inputsindexofscale);
				scaleminperinput = Collections.min(inputsindexofscale);
				//System.out.println("the scalemaxperinput is : "+scalemaxperinput+" and the min is "+scaleminperinput);
				scalebinsperinput1 = (int) Math.floor((scaleminperinput ) / hough_Scale);
				scalebinsperinput2 = (int) Math.ceil((scalemaxperinput ) / hough_Scale );

				//System.out.println("The scalebinsperinput is : "+scalebinsperinput1+" And the other one is "+scalebinsperinput2);


				count[i] = 0;
				//mainindex[i] = new int [scalebinsperinput2 - scalebinsperinput1][];
				for (int k = scalebinsperinput1; k < scalebinsperinput2; k++)
				{
					ArrayList<Integer> inputsindexofxloc = new ArrayList<Integer>();
					ArrayList<Integer> inputsindexofyloc = new ArrayList<Integer>();
					ArrayList<Integer> inputsofperindex = new ArrayList<Integer>();
					for(int j =0; j < inputsindexofscale.size(); j ++)
					{
						if (((inputsindexofscale.get(j) / hough_Scale) >= k) && ((inputsindexofscale.get(j) / hough_Scale) < (k+1)))
						{
							inputsindexofxloc.add((int)input2[(inputsindexoforientation.get(j) * featureSize) + 2]);
							inputsindexofyloc.add((int)input2[(inputsindexoforientation.get(j) * featureSize) + 3]);
							inputsofperindex.add(inputsindexoforientation.get(j));
						}
					}
					//System.out.println(" x "+inputsindexofxloc +" y "+inputsindexofyloc+" and also "+inputsindexoforientation);

					if(inputsofperindex.size() != 0)
					{
						//System.out.println("the size of the inputsindexofxloc is "+inputsindexofxloc.size());
						//ArrayList<Integer> copyinputsindexofxloc = new ArrayList<Integer>(inputsindexofxloc);
						//ArrayList<Integer> copyinputsindexofyloc = new ArrayList<Integer>(inputsindexofyloc);
						//Collections.sort(copyinputsindexofxloc);
						//Collections.sort(copyinputsindexofyloc);
						float locxmaxperinput = Collections.max(inputsindexofxloc);
						float locymaxperinput = Collections.max(inputsindexofyloc);
						//System.out.println("the max x loc is "+locxmaxperinput+" and the max y is "+locymaxperinput);
						count[i] = (int) (count[i] + (locxmaxperinput * locymaxperinput));
						inputsofxloc.add(inputsindexofxloc);
						inputsofyloc.add(inputsindexofyloc);
						inputsofindex.add(inputsofperindex);
						//mainindex[i][k] = new int [inputsindexofxloc.size()];
						//added 2/4/11
						totalCount+= count[i];
						//
						//System.out.println(totalCount);
						inputsindexofxloc = null;
						inputsindexofyloc = null;
						inputsofperindex = null;
						/*for(int j = 0; j < count[i]; j++)
						{ 
							accscaleloc.add(0);   					//initialize
						}*/
						//System.out.println("the size would be "+count[i]+"&"+totalCount);
					}
				}
				//System.out.println(totalCount);
				inputsindexofscale = null;
				int addr = 0;
				//base = 0;
				//System.out.println("the size of the inputsofxloc ie the number of scales: "+inputsofxloc.size());
				for (int k = 0; k < inputsofxloc.size(); k++)
				{
					HashMap<Integer, Integer> accumulatorMap = new HashMap<Integer, Integer>();
					ArrayList<Integer> maxlocxval = new ArrayList<Integer>();
					ArrayList<Integer> maxlocyval = new ArrayList<Integer>();

					ArrayList<Integer> perindex = new ArrayList<Integer>();
					perindex = inputsofindex.get(k);
					ArrayList<Integer> xf = new ArrayList<Integer>();
					xf = inputsofxloc.get(k);
					ArrayList<Integer> yf = new ArrayList<Integer>();
					yf = inputsofyloc.get(k);
					
					for(int j =0; j < inputsofxloc.get(k).size(); j ++)
					{
						
						int a = (xf.get(j)).intValue() / (width/hough_Loc);
						
						int b =  (yf.get(j)).intValue() / (height/hough_Loc);
						int c =  (((b) * hough_Loc) + a);
						addr = (base + c);
						//2/4/11
						if(accumulatorMap.containsKey(addr))
						{
							int z = accumulatorMap.get(addr);
							z++;
							accumulatorMap.remove(addr);
							accumulatorMap.put(addr, z);
						}
						else
						{
							accumulatorMap.put(addr, 1);
						}

						//int z = accscaleloc.get(addr);
						//z++;
						//accscaleloc.set(addr,z);
						//
						mainindex[perindex.get(j)] = addr;
						//System.out.println("the add and z are"+ addr+" ..fd "+z+" and the index in input is : "+perindex.get(j));
						//yf = null;
						//xf = null;

					}
					maxlocxval = inputsofxloc.get(k);
					maxlocyval = inputsofyloc.get(k);
					Collections.sort(maxlocxval);
					Collections.sort(maxlocyval);
					//base = (int) (base + (Collections.max(maxlocxval)) * (Collections.max(maxlocyval)));
					base = (int) (base + (hough_Loc) * (hough_Loc));
					maxlocxval = null;
					maxlocyval = null;
					accumlatorPerScale.add(accumulatorMap);
				}
				inputsofxloc = null;
				inputsofyloc = null;
				inputsofindex = null;
				//2/4/2011
				//accorientation.add(accscaleloc);
				//System.out.println("map acc "+accumulatorMap.size());
				accAccumulator.add(accumlatorPerScale);
				//
				//accscaleloc = null;
				accumlatorPerScale = null;
			}
			inputperorientation.add(inputsindexoforientation);
		}
		//2/4/11
		//for (int z = 0; z < accorientation.size(); z++)
		for (int z = 0; z < accAccumulator.size(); z++)
		{
			ArrayList<HashMap<Integer, Integer>> accScaleForInput = new ArrayList<HashMap<Integer, Integer>>();
			accScaleForInput = accAccumulator.get(z);
			for(int scales = 0; scales < accScaleForInput.size(); scales++)
			{
				HashMap<Integer, Integer> accscalelocforinput = new HashMap<Integer, Integer>();
				//accscalelocforinput = accorientation.get(z);
				accscalelocforinput = accScaleForInput.get(scales);

				//System.out.println(accscalelocforinput.size());
				for (Object key: accscalelocforinput.keySet())
				{
					if (accscalelocforinput.get(key) > hough_Th)
					{
						//dec09
						//for(int y = 0 ; y < mainindex.length; y++)
						for(int xy = 0 ; xy < mainindex.length; xy++)
						{
						//for(int y = 0 ; y < mainindex[xy].length; y++)
						//{
							//dec 09
							//if(mainindex[y] == x)
							//if(mainindex[z][y] == x)
							if(mainindex[xy] == Integer.parseInt((String) key.toString()))
							{
								finalindexes.add (xy);
								//System.out.println("the index is " + xy+ "and the add is "+key);
							}
						//}
					}
					}
				}
				accscalelocforinput = null;			
			}
		}
		//HashSet<Integer> finalIndexes = finalindexes.hashCode();
		//
		accorientation = null;
		features = new float[finalindexes.size() * featureSize];
		for (int g = 0; g < finalindexes.size() ; g++)
		{
			for (int h = 0; h < featureSize; h++)
			{
				features[(g * featureSize) + h] = input2[(finalindexes.get(g) * featureSize) + h];
				//System.out.println("the add locations are :"+features[(g * featureSize) + h]);
			}
		}
		//input2 = null;
		System.out.println("the size of Hough-SIFT is "+finalindexes.size());
		if(outputfile != null)
		{
			try{
				outputfile.write("the size of Hough-SIFT is "+finalindexes.size()+"\n");
				outputfile.flush();
			}catch(java.io.IOException e)
			{ 
				System.out.println("Writing to output file failed.");
			}

		}
		finalindexes = null;
		return features;
	}

	/*
	private void calculateParameters(float[] fs) 
	{
		// TODO Auto-generated method stub
		for (int i = 0; i < fs.length / featureSize; i++)
		{
			scale[i] = fs[(i*featureSize) + 0];
			orientation[i] = fs[(i*featureSize) + 1];
			xlocation[i] = fs[(i*featureSize) + 2];
			ylocation[i] = fs[(i*featureSize) + 3];
		}
	}

	private double getMaxScale(float[] scale) 
	{
		// TODO Auto-generated method stub
		clonescale = scale;
		Arrays.sort(clonescale);
		maxscale = clonescale[ (clonescale.length) - 1];
		return maxscale;
	}
	 */
}
